package com.andlabs.gd.base;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;

import com.andlabs.gd.R;

public abstract class GameActivity extends Activity implements OnTouchListener, SensorEventListener, SurfaceHolder.Callback {

	/**
	 * the game's (surface)view
	 */
	private SurfaceView mGameView;
	/**
	 * determines whether the main game loop is running or not
	 */
	private boolean mMainLoopRunning;
	/**
	 * the surface holder of the game view
	 */
	private SurfaceHolder mSurfaceHolder;
	/**
	 * the main game rendering thread
	 */
	private Thread mRenderingThread;
	/**
	 * the main game simulation thread
	 */
	private Thread mSimulationThread;
	/**
	 * listener for the game's simulation
	 */
	private SimulationListener mSimulationListener;
	/**
	 * listener for the game's rendering
	 */
	private RenderingListener mRenderingListener;
	
	/**
	 * the delta time between the last simulation iteration and now
	 */
	private float mDeltaTime = 0;
	/**
	 * the timestamp of the last simulation iteration
	 */
	private long mLastSimulationIteration = 0;	
	/** 
	 * touch coordinates
	 **/
    private int mTouchX, mTouchY;
    /**
     * touched state
     */
    private boolean mIsTouched;
    /**
     * acceleration on the 3 axis 
     **/
    private float[] mAcceleration = new float[3];    
    /**
     * all these are for calculating the fps
     */
    private int mFPS = 0;
    private int mFPSCount = 0;
    private float mTimeElapsed = 0;
    private long mLastRenderingIteration = 0;
	public boolean mIsMoving;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mGameView = new SurfaceView(this);
        mSurfaceHolder = mGameView.getHolder();
        mSurfaceHolder.addCallback(this);
        mGameView.setOnTouchListener(this);
        setContentView(mGameView);        
     
        mMainLoopRunning = true;
                
        mRenderingThread = new Thread(new Runnable(){
        	public void run() {
        		render();
        	}
        }, "Rendering Thread");        
        
        mSimulationThread = new Thread(new Runnable(){
        	public void run() {        	
        		simulate();
        	}
        }, "Simulation Thread");
        
        SensorManager manager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0){
        	final Sensor accelerometer = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);
            manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME );
        }
    }	
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		 if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE){
	         mTouchX = (int)event.getX();
	         mTouchY = (int)event.getY();
	         mIsTouched = true;
	         
	         if(event.getAction() == MotionEvent.ACTION_MOVE){
	        	 mIsMoving = true;
	         }
	     }

        if( event.getAction() == MotionEvent.ACTION_UP ){
             mIsTouched = false;
             mIsMoving = false;
        }

        try{
             Thread.sleep( 30 );
        }catch(Exception ex){
        	//zonk!
        } 
        
        return true;
	}
	
	private void render(){
		if(mRenderingListener != null){
			mRenderingListener.initializeSplash();
		}
		new Thread(new Runnable(){
			@Override
			public void run() {
        		if(mRenderingListener != null){
        			mRenderingListener.initializeRendering();
        		}        				
			}
		}).start();
		Canvas c;
		while(mMainLoopRunning){
			c = null;
	        try {
	        	c = mSurfaceHolder.lockCanvas();
	            synchronized (mSurfaceHolder) {
	            	if(mRenderingListener != null){
	            		if(mRenderingListener.isInitializingRendering() || mSimulationListener == null || mSimulationListener.isInitializingSimulation()){
	            			mRenderingListener.renderSplash(c);
	            		} else{
	            			mRenderingListener.render(c);
	            		}
	            	}
	            }
	        } finally {
	            // do this in a finally so that if an exception is thrown
	            // during the above, we don't leave the Surface in an
	            // inconsistent state
	            if (c != null) {
	                mSurfaceHolder.unlockCanvasAndPost(c);
	            }
	        }    		        
			
	        //the fps calculation
			mFPSCount++;
			mTimeElapsed += (System.nanoTime() - mLastRenderingIteration) / 1000000000.0;			
			if(mTimeElapsed >= 1){
				mFPS = mFPSCount;
				Log.d("GameActivity", "FPS: " + mFPS);
				mFPSCount = 0;
				mTimeElapsed = 0;
			}			
			mLastRenderingIteration = System.nanoTime();
		}
	}
	
	private void simulate(){
		
		if(mSimulationListener != null){
        	mSimulationListener.initializeSimulation();
		}
        
		while(mMainLoopRunning){
			if(mSimulationListener != null && !mSimulationListener.isInitializingSimulation() && mRenderingListener != null && !mRenderingListener.isInitializingRendering()){
				mDeltaTime = (System.nanoTime() - mLastSimulationIteration) / 1000000000.0f;
				mLastSimulationIteration = System.nanoTime();
				mSimulationListener.simulationIteration(mDeltaTime);			 
			}
		}
	}
	
	public void startIterations(){
		mMainLoopRunning = true;
		mRenderingThread.start();
        mSimulationThread.start();     
	}
	
	public void stopIterations(){
		mMainLoopRunning = false;
		boolean retry = true;
		//stop the rendering thread
		while (retry) {
			try {
	        	mRenderingThread.join();
	            retry = false;
	        } catch (InterruptedException e) {
	            // we will try it again and again...
	        }
	    }
		
		//stop the simulation thread
		retry = true;
		while (retry) {
			try {
	        	mSimulationThread.join();
	            retry = false;
	        } catch (InterruptedException e) {
	            // ...
	        }
	    }
	}

	public void setSimulationListener(SimulationListener simulationListener) {
		mSimulationListener = simulationListener;
	}

	public void setRenderingListener(RenderingListener renderingListener) {
		mRenderingListener = renderingListener;
	}
	
	/**
	 * @return the frames per second
	 */
	public int getFPS(){
		return mFPS;
	}
	
	/** 
	 * Called when the accuracy of the Sensor has changed.
	 * @param sensor
	 * @param accuracy
	 */
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy){
	        // we ignore this
	}

	/**
	 * Called when the sensor has new input
	 * @param event The SensorEvent
	 */
	@Override
	public void onSensorChanged(SensorEvent event){
	        System.arraycopy( event.values, 0, mAcceleration, 0, 3 );
	}
	
	/**
	 * @return the delta time in seconds
	 */
	public float getDeltaTime( ){
	        return mDeltaTime;
	}

	/**
	 * @return the last known touch coordinate on the x axis
	 */
	public int getTouchX( ){
	        return mTouchX;
	}

	/**
	 * @return the last known touch coordinate on the x axis
	 */
	public int getTouchY( )	{
	        return mTouchY;
	}

	/**
	 * @return whether the touch screen is touched or not
	 */
	public boolean isTouched( )	{
	        return mIsTouched;
	}

	/**
	 * @return the acceleration on the x-Axis of the device
	 */
	public float getAccelerationOnXAxis( ){
	        return mAcceleration[0];
	}

	/**
	 * @return the acceleration on the x-Axis of the device
	 */
	public float getAccelerationOnYAxis( ){
	        return mAcceleration[1];
	}

	/**
	 * @return the acceleration on the x-Axis of the device
	 */
	public float getAccelerationOnZAxis( ){
	        return mAcceleration[2];
	}
	
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		//implement something, if this can happen in your game 
	}

	/**
	 * called when the surface created
	 */
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		//start the two main threads
		startIterations();
	}

	/**
	 * called when the surface is destroyed
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		//stop the two main threads
		stopIterations();
	}
}